home *** CD-ROM | disk | FTP | other *** search
/ AOL File Library: 4,401 to 4,500 / aol-file-protocol-4400-4401-to-4500.zip / AOLDLs / PDA-Newton Development / ND Life 1.2 (two versions) / life-12.sit / life.nwt < prev    next >
Text File  |  1994-11-12  |  19KB  |  570 lines

  1. Notes
  2. {labels: 'NIL, viewFont: 10241} // nil=Unfiled, or specify a folder, e.g., 'Business.  remove viewFont for current Styles default
  3. //ERASE! remove initial // to erase existing entries in this folder first
  4.  
  5. life.nwt -- Slurpee format
  6. 11/12/94
  7. Copyright 1994 S. Weyer.  All Rights Reserved Worldwide.
  8.  
  9. Keywords: Life, cellular automata, mathematical simulation, Conway
  10.  
  11. This Newt source version of "the game of Life" and associated NTK packages are
  12. freeware and may be distributed freely as long as the files "life.nwt" is
  13. included and unmodified.  You are free to make modifications for your own
  14. use.
  15.  
  16. Contents:
  17. - life-12.pkg  -- general implementation (Life 1.2)
  18. - life-12b.pkg -- a more optimized (bitmap-based) version (Life 1.2B)
  19. - life.nwt -- Newt source for Life 1.2 [this file]
  20. (Mac format text file in .sit/.hqx archives; DOS text in .zip archive)
  21. ----------
  22. Development and "Newt"
  23.  
  24. If you would like to construct and modify Life directly on your Newton, obtain
  25. the latest version of "Newt" (newt-devenv-24), the native Newton development
  26. environment (shareware), and "Slurpee" (slurpee-12) a text/data transfer
  27. utility (freeware) from your favorite Newton server/archive (internet (uiowa,
  28. amug,...), AOL, Compuserve, eWorld). See "Develop" and "Author" help pages.
  29. Contact me if you have trouble finding Newt, using it initially, or have
  30. questions about registering it.  (An interesting note: I developed these
  31. versions first using Newt, then created the NTK versions).
  32.  
  33. Registered Newt users receive an introductory manual and floppy of several
  34. dozen examples, including several versions of Life:
  35. - life.nwt   (source for Life 1.2) [same as this file]
  36. - lifeb1.nwt (source for Life 1.2B)
  37. - lifeb0.nwt (an early, more readable but much slower version of Life 1.2B)
  38. ----------
  39. Revision History:
  40.  
  41. Life 1.2B (11/12/94)
  42. - provides scrub gesture for clear
  43. - allows grid size to be selected by user
  44. - uses all visible cells (1.2 does not use first&last row&col)
  45. - generates faster (1.5 - 3.5x Life 1.2) and allows larger grids
  46.   by using a bitmap data structure for storage and drawing
  47.  
  48. Life 1.2 (11/12/94)
  49. - modifies symbol names/program structure to be more consistent with 1.2B
  50. - fixes button highlighting glitch (sibling order)
  51.  
  52. Life 1.1 (10/15/94)
  53. - uses 0/1 for cell values rather than nil/true
  54. - allows patterns to be selected/added
  55. - adds a grid (when stopped)
  56.  
  57. Life 1.0 (9/18/94)
  58. - adds a "Clear" and "Next" button
  59. - adds a generation counter.
  60. - adds a "Repeat" button -- which invokes "Next" in the background until
  61.   you tap "Stop" (or all cells are empty)
  62. - adds an "Info" button and help book
  63. - avoids allocating/examining empty rows/columns where possible to
  64.   improve speed drawing and generating
  65.  
  66. Life "0.1" (3/94)
  67. - see "Life with NewtonScript" by David Betz in Byte, March 1994, pp. 191-194.
  68.   This article provided inspiration, an initial framework and a few main methods.
  69.  
  70. These versions are quite usable for small patterns, but not blindingly fast
  71. for more sparse or spreadout patterns (though Life 1.2B handles
  72. larger/sparser patterns better) -- the emphasis is on the complete
  73. application and learning programming.
  74.  
  75. Possible future enhancements:
  76. - allow patterns to be saved in/retrieved from a soup
  77. - save preferences for grid size, etc.
  78. - recompile "nextGeneration" method with NTK RISC compiler when available
  79.  
  80. If you would like to encourage me to develop and distribute this and other
  81. examples, I encourage you to register Newt (kind words also appreciated).
  82. ----------
  83. Using Life
  84.  
  85. Tap the info button to access the built-in help book.
  86.  
  87. Some classic patterns to try (some in pattern picker):
  88.   OOO     (blinker -- flips)
  89.  
  90.    O
  91.   O       (glider -- moves down&left 1 in 4 steps)
  92.   OOO
  93.  
  94.   OO
  95.   OO      (block -- stable)
  96.  
  97.    OO
  98.   O  O    (beehive -- stable)
  99.    OO
  100.  
  101.     
  102.   OOOOO   (turns into "traffic lights" (4 blinkers) in 9 steps)
  103.  
  104.  OOOOOOO  (turns into "honey farm" (4 beehives) ...)
  105.  
  106. OOOOOOOOO (double traffic lights...)
  107. ----------
  108. Transferring and Building Life
  109.  
  110. Use your favorite transfer mechanism to move the text from your desktop system
  111. to the Newton Notepad. This file is formatted to be used most easily with
  112. Slurpee. ----- delimits separate notes.  To save on Notepad space, you may
  113. wish to make a copy of this file, and delete these comment paragraphs upto
  114. ----- preceding "Life" definition.
  115.  
  116. To build the Life application, install Newt (2.4 is the latest version at time
  117. of this release). (If you would like to save the application to run it
  118. standalone later, also install the matching version of RUNewt, e.g., 2.4).
  119.  
  120. Start Newt, select the folder at top where you stored the source.
  121.  
  122. Tap Expr, select :doObj('build,'Life), tap Eval
  123.  
  124. Some simple modifications you might try:
  125. - change sizeX to a smaller or larger number
  126. - change MakeOval to MakeRect in viewDrawScript
  127. - add other patterns in patternPicker.patterns array (and names in labelCommands)
  128. - add other pages to the helpbook
  129. - change the rules (countNeighbors, nextGeneration)
  130.  
  131. If you would like to run the Life application later without having to
  132. recompile the sources in Newt, and you have RUNewt installed, tap the Save
  133. button (in Eval Controls) when Life is open.
  134.  
  135. Note1: Although you can distribute Newt-generated apps to other Newtons via
  136. beaming or email(eWorld/NewtonMail), there is currently no mechanism for
  137. making these apps into external "package files" -- thus, I have included an
  138. NTK-package version (Life-12.pkg) for those who would like to try Life
  139. without building it; I also have included Life-12b.pkg since the source for
  140. that newer, faster version is available only to registered users.
  141.  
  142. Note2: there is currently a bug in using help books in RUNewt (2.4) apps.
  143. ----------
  144. Objects:
  145.  
  146. Life -- the application (floating view)
  147.   [split into several sections to make editing on Newton more manageable]
  148. LifeDraw -- an embedded view to handle drawing
  149. titleObj -- title at top of Life view
  150. status -- status bar at bottom of Life view
  151. clearButton, nextButton, repeatButton, zinfoButton -- embedded in status
  152. patternPicker -- picker for selecting patterns
  153. numGen -- generation counter
  154. helpBook -- help book data structure
  155. helpView -- dynamic "Tiny Tim" help reader view
  156. ----------
  157. Life
  158. //:doObj('build, 'Life)
  159. {// the main Life application
  160. viewClass: 'clView,
  161. viewBounds: NIL,    // computed in viewSetupFormScript
  162. viewFlags: 5,       // vVisible + vApplication
  163. viewFormat: 337,    // vfFillWhite + vfFrameBlack + vfPen*1,
  164. declareSelf: 'base, // for close box
  165. title: "Life",
  166.  
  167. // SET THIS
  168. sizeX:     16, // width of universe (in editable cells). (does not include 1st & last empty cols)
  169.  
  170. // these are computed from sizeX in viewSetupFormScript
  171. sizeY:    NIL, // height of universe
  172. cellSize: NIL, // diameter of a cell
  173. vSizeX:   NIL, // width of LifeDraw
  174. vSizeY:   NIL, // height of LifeDraw
  175.  
  176. firstRow: NIL, // to avoid dead rows above
  177. lastRow:  NIL, // to avoid dead rows below
  178. firstCol: NIL, // ditto for columns
  179. lastCol:  NIL,
  180. universe: NIL, // universe array of arrays (created by makeUniverse)
  181. pattern:  NIL, // defaults to dot. set by patternPicker
  182. nGen:       0, // number of generations (clear =0, nextGeneration +1)
  183.  
  184. kDrawTop:  20, // extra space above LifeDraw
  185. kDrawBot:  35, // below
  186. kMaxAppWidth: 240,
  187.  
  188. makeUniverse: func()
  189.     Array(sizeY + 2, NIL), // leave rows NIL until needed
  190.  
  191. viewSetupFormScript: func()
  192.     begin
  193.     self.Life := self; // for later standalone access
  194.     local ht, ap:= GetAppParams(); // configure to current MP screen size
  195.     self.viewBounds := RelBounds(
  196.         ap.AppAreaLeft,ap.AppAreaTop,
  197.         MIN(ap.AppAreaWidth, kMaxAppWidth), ht := MIN(ap.AppAreaHeight,336));
  198.  
  199.     cellSize := kMaxAppWidth DIV (sizeX+2);
  200.     sizeY := ((ht - kDrawTop - kDrawBot) DIV cellSize) - 2;
  201.     vSizeX := cellSize*(sizeX+2);
  202.     vSizeY := cellSize*(sizeY+2);
  203.     universe := :makeUniverse();
  204.     firstRow := lastRow := firstCol := lastCol := NIL;
  205.     end,
  206. }
  207. ----------
  208. Life.idleStuff~
  209. {//some additional Life methods/slots related to idling ("Repeat")
  210. viewIdleScript: func()
  211.     if not stopped
  212.     then begin
  213.         :nextGeneration(); // generate
  214.         if firstRow
  215.         then delayMSecs // reschedule
  216.         else begin // empty universe
  217.             SetValue(status.repeatButton, 'text, "Repeat");
  218.             stopped := TRUE; 
  219.             NIL // turn off idling by returning nil
  220.             end;
  221.         end,
  222.  
  223. delayMsecs: 40, // allow enough milliseconds in between for taps
  224. stopped: TRUE,
  225. }
  226. ----------
  227. Life.lifeStuff~
  228. {// some additional Life methods, specific to Life
  229. makeRow: func()
  230.     Array(sizeX + 2, 0),   // initial values 0 (rather than NIL)
  231.  
  232. clear: func()
  233.     begin
  234.     universe := :makeUniverse();
  235.     firstRow := lastRow := firstCol := lastCol := NIL; // all empty
  236.     LifeDraw:Dirty(); // redraw next idle via viewDrawScript
  237.     nGen := 0;
  238.     SetValue(numGen,'text,"Gen: 0");
  239.     end,
  240.  
  241. countNeighbors: func(x,y,occ)
  242.     begin // count number of nearby (8) cells that are filled
  243.     local yy, row, cnt := -occ; // don't count center cell
  244.     for yy := y-1 to y+1
  245.     do if row := universe[yy]
  246.         then cnt := cnt + row[x-1]+row[x]+row[x+1];
  247.         //for xx := x-1 to x+1 do if row[xx] then cnt := cnt+1;
  248.     cnt;
  249.     end,
  250. }
  251. ----------
  252. Life.nextGeneration
  253. func() // a separate entry for editing
  254. if firstRow // if no cells occupied, nothing to generate
  255. then begin
  256.     local newUniverse := :makeUniverse(); // new empty universe
  257.     local x, y, row, occupied, newRow, neighbors, newFirstRow, newLastRow;
  258.     local newLastCol  := fcol := max(firstCol-1,1);
  259.     local newFirstCol := lcol := min(lastCol+1,sizeX), frx, lrx;
  260.     for y := max(firstRow-1,1) to min(lastRow+1,sizeY) // skip some dead rows before/after active area
  261.     do begin
  262.         newRow := frx := lrx := NIL;
  263.         row := universe[y];
  264.         for x := fcol to lcol // skip some dead columns before/after active area
  265.         do begin
  266.             occupied := if row then row[x] else 0;
  267.             neighbors := :countNeighbors(x,y,occupied);
  268.             if neighbors=3 or (occupied>0 and neighbors=2)
  269.             then begin
  270.                 if not newRow // create a new row?
  271.                 then newRow := :makeRow();
  272.                 newRow[x] := 1;
  273.                 newLastRow := y;
  274.                 if not frx then frx := x;
  275.                 lrx := x;
  276.                 end;
  277.             end;
  278.         if not newFirstRow then newFirstRow := newLastRow;
  279.         newUniverse[y] := newRow;
  280.         if frx then newFirstCol := min(newFirstCol,frx);
  281.         if lrx then newLastCol  := max(newLastCol, lrx);
  282.         end;
  283.     universe := newUniverse;
  284.     firstRow := newFirstRow;
  285.     lastRow  := newLastRow;
  286.     firstCol := newFirstCol;
  287.     lastCol  := newLastCol;
  288.     LifeDraw:Dirty();
  289.     SetValue(numGen, 'text, "Gen:" && (nGen := nGen+1));
  290.     end
  291. ----------
  292. Life+LifeDraw
  293. {// a drawing area (stepchild of Life)
  294. viewClass: 'clView,
  295. viewFlags:  513, // vVisible + vClickable
  296. viewJustify: 16, // vjParentCenterH + vjParentTopV
  297. viewFormat: 337, // vfFillWhite + vfFrameBlack + vfPen*1,
  298.  
  299. viewSetupFormScript: func()
  300.     self.viewBounds := RelBounds(0,kDrawTop,vSizeX,vSizeY),
  301.  
  302. viewDrawScript: func()
  303.     begin
  304.     local x, y, row, drawMode, y1, y2;
  305.     if stopped
  306.     then begin // add grid lines
  307.         drawMode := '{ // for lines
  308.             penSize: 1,
  309.             penPattern: 3, // vfGray
  310.             };
  311.         for y := cellSize to vSizeY-cellSize by cellSize
  312.         do :DrawShape(MakeLine(0,y,vSizeX,y), drawMode);
  313.         for x := cellSize to vSizeX-cellSize by cellSize
  314.         do :DrawShape(MakeLine(x,0,x,vSizeY),  drawMode);
  315.         end;
  316.     drawMode := '{ // for ovals
  317.         transferMode: 0, // modeCopy
  318.         fillPattern:  5, // vfFillBlack
  319.         };
  320.     if firstRow
  321.     then for y := firstRow to lastRow
  322.         do if row := universe[y]
  323.             then begin
  324.                 y1 := y*cellSize; y2 := y1+cellSize; // constant for row
  325.                 for x := firstCol to lastCol
  326.                 do if row[x]>0
  327.                 then :DrawShape(
  328.                         MakeOval(x*cellSize,y1,(x+1)*cellSize,y2),
  329.                         drawMode);
  330.                 end;
  331.     end,
  332. }
  333. ----------
  334. Life.LifeDraw.viewClickScript
  335. func(unit) // a separate entry for editing
  336.     begin
  337.     local gb := :GlobalBox(), row, y;
  338.     local ylen := if pattern then length(pattern) else 0, xlen, rlen;
  339.     // convert to local, grid coordinates
  340.     local clickX := x2 := (GetPoint(0, unit) - gb.left) div cellSize; // 0=firstX
  341.     local clickY := y2 := (GetPoint(1, unit) - gb.top)  div cellSize; // 1=firstY
  342.  
  343.     if clickX >=1 and clickX <= sizeX and
  344.        clickY >=1 and clickY <= sizeY
  345.     then begin
  346.         if ylen>0
  347.         then begin // copy patterns
  348.             if y2+ylen > sizeY
  349.             then ylen := sizeY - y2 + 1; // clip pattern at bottom
  350.             y2 := y2 + ylen - 1;
  351.             xlen := length(pattern[0]);  // note: uses length of first row as max width of pattern
  352.             if x2+xlen > sizeX
  353.             then xlen := sizeX - x2 + 1; // clip pattern at right
  354.             x2 := x2 + xlen - 1;
  355.             for y := 0 to ylen-1
  356.             do begin
  357.                 rlen := min(length(pattern[y]),xlen); // len of current row in pattern
  358.                 if not (row := universe[clickY + y])  // create row if necessary
  359.                 then universe[clickY+y] := row := :makeRow();
  360.                 ArrayMunger(row,clickX,rlen,pattern[y],0,rlen); // copy row
  361.                 end;
  362.             end
  363.  
  364.         else // add a dot
  365.             if row := universe[clickY] // row exists?
  366.         then row[clickX] := 1 - row[clickX] // toggle dot in existing row
  367.         else begin // add to new row
  368.             universe[clickY] := row := :makeRow(); // wasn't allocated yet
  369.             row[clickX] := 1;
  370.             end;
  371.  
  372.         // expand live area
  373.         firstRow := if firstRow then min(firstRow,clickY) else clickY;
  374.         lastRow  := if lastRow  then max(lastRow,y2)  else y2;
  375.         firstCol := if firstCol then min(firstCol,clickX) else clickX;
  376.         lastCol  := if lastCol  then max(lastCol,x2)  else x2;
  377.         end;
  378.  
  379.     :Dirty(); // returns true
  380.     end
  381. ----------
  382. Life+titleObj
  383. {// title at top (uses Life.title)
  384. _proto: 'protoTitle,
  385. }
  386. ----------
  387. ButtonBounds
  388. func(width)
  389. // used for adding embedded buttons to status
  390. // this is defined as a Newt development-time method
  391. // once the buttons are initialized and added to status,
  392. // it really isn't needed anymore and isn't saved with the app
  393. begin
  394.     local left1:=25, top:=2, spacing:=6, bottom:=15;
  395.     if width > 0
  396.     then SetBounds(spacing,  top, spacing + width, bottom)   // other buttons
  397.     else SetBounds(left1,    top, left1 - width,   bottom);  // first button
  398. end
  399. ----------
  400. Life+status
  401. {//status bar with clock and closebox
  402. _proto: 'protoStatus,
  403. }
  404. ----------
  405. Life.status+clearButton
  406. {_proto: 'protoTextButton,
  407. viewBounds: :ButtonBounds(-40), // first button (stepchild) in status area
  408. text: "Clear",
  409. buttonClickScript: func() :clear(),
  410. }
  411. ----------
  412. Life.status+nextButton
  413. {_proto: 'protoTextButton,
  414. viewBounds: :ButtonBounds(40), // second button
  415. viewJustify: 8389638, // vjParentLeftH + vjParentTopV + vjSiblingRightH + oneLineOnly + vjCenterH + vjCenterV,
  416. text: "Next",
  417. buttonClickScript: func() :nextGeneration(),
  418. }
  419. ----------
  420. Life.status+repeatButton
  421. {_proto: 'protoTextButton,
  422. viewBounds: :ButtonBounds(40), // third button
  423. viewJustify: 8389638,
  424. text: "Repeat",
  425. buttonClickScript: func()
  426.     if firstRow
  427.     then if stopped
  428.         then begin
  429.             Life:setupIdle(delayMsecs); // start background process
  430.             stopped := NIL;
  431.             SetValue(self,'text,"Stop");
  432.             end
  433.         else begin
  434.             Life:setupIdle(0); // halt background process
  435.             stopped := TRUE;
  436.             SetValue(self,'text,"Repeat");
  437.             LifeDraw:Dirty(); // redraw to make grid visible
  438.             end,
  439. }
  440. ----------
  441. Life+patternPicker
  442. {//a picker for pre-built patterns
  443. _proto: 'protoLabelPicker,
  444. viewBounds: RelBounds(2, -32, 140, 16),
  445. viewJustify: 8388736, // vjLeftH + vjParentBottomV + oneLineOnly,
  446. patterns: [// order matches names in labelCommands
  447.     NIL,         // none=dot
  448.     NIL,         // menu separator line --------
  449.     [[0,1,0,],   // glider (note: 1st line of pattern should be max width (pad right with 0s if nec)
  450.      [1,],       //   subsequent rows can omit trailing 0s
  451.      [1,1,1,]],
  452.     [[1,1,1,]],  // blinker
  453.     [[1,1],      // block
  454.      [1,1]],
  455.     [[0,1,1,0,], // beehive
  456.      [1,0,0,1,],
  457.      [0,1,1,]],
  458.     ],
  459. labelCommands: ["<dot>",'pickSeparator, "glider", "blinker", "block", "beehive",],
  460. labelActionScript: func(i)
  461.     pattern := patterns[i], // for use in viewClickScript
  462. textSetup: func() // name of current pattern
  463.     labelCommands[ArrayPos(patterns,pattern,0,nil)],
  464. }
  465. ----------
  466. Life+numGen
  467. {// a generation counter
  468. _proto: 'protoStaticText,
  469. viewBounds: RelBounds(180,-32,50,16), // centered, just above status bar
  470. viewJustify: 8388736,  // vjLeftH + vjParentBottomV + oneLineOnly
  471. text: "Gen: 0",
  472. }
  473. ----------
  474. Life.helpStuff~
  475. {// additional Life methods to support help book
  476. helpBook: deepclone(helpBookTemplate), // copy from Newt
  477. helpView: NIL,
  478.  
  479. openHelp: func()
  480.     if helpBook
  481.     then begin
  482.         if not helpView
  483.         then helpView := BuildContext(
  484.             {_proto: GetRoot().TinyTim._proto,
  485.             bookRef: helpBook});
  486.         GetRoot().TinyTim:Close();   // in case system help is open
  487.         helpView:openManual(helpBook);
  488.         end,
  489.  
  490. viewQuitScript: func()
  491.     begin
  492.     if helpView then helpView:close();
  493.     helpView := NIL;
  494.     end,
  495. }
  496. ----------
  497. Life.status+zinfoButton
  498. {_proto: 'protoTextButton,
  499. viewBounds: :ButtonBounds(40), // fourth button (zinfo sorts 4th)
  500. viewJustify: 8389638,
  501. text: "Info",
  502. buttonClickScript: func() :openHelp(), // open help book
  503. }
  504. ----------
  505. Life.helpBook+page1
  506. .# first help book page
  507. .subject 1
  508. Describe Life
  509. .story
  510. Life is a mathematical game by John Conway described originally in Scientific American in Oct. 1970.
  511.  
  512. Cells in the current generation are examined simultaneously in order to create the next generation using the following rules:
  513. - Survival: A cell with 2 or 3 neighbors survives.
  514. - Death: A cell with 1 or less, or 4 or more neighbors, "dies".
  515. - Birth: An empty cell with exactly 3 neighbors will be "born".
  516. ----------
  517. Life.helpBook+page2
  518. .subject 1
  519. Understand the Code
  520. .story
  521. This version of Life adds objects, methods and optimizations to an implementation discussed in "Life with NewtonScript" by David Betz in Byte, March 1994, pp. 191-194.
  522.  
  523. As a precursor/alternative to buying APDA's Newton ToolKit (NTK), you could register for Newt (see Develop) to obtain a manual and more NewtonScript application examples.
  524. ----------
  525. Life.helpBook+page3
  526. .subject 1
  527. Distribute
  528. .story
  529. This version of Life is freeware and may be distributed freely as long as the file "life.nwt" is included and unmodified.  You are free to make modifications for your own use.
  530.  
  531. Copyright 1994 S. Weyer. All Rights Reserved Worldwide.
  532. ----------
  533. Life.helpBook+page4
  534. .subject 1
  535. Use
  536. .story
  537. \uFC01\u pattern -- select a pattern
  538.  
  539. <tap> in cell area to add a pattern, or add/remove individual cells
  540.  
  541. Clear -- clear all cells, reset gen. counter
  542.  
  543. Next  -- evolve a single generation
  544.  
  545. Repeat -- evolve until all cells are empty or you tap Stop
  546.  
  547. Info -- open this help book
  548. ----------
  549. Life.helpBook+page5
  550. .subject 1
  551. Develop
  552. .story
  553. If you are interested in object-oriented programming in NewtonScript, developing applications on your Newton, or graphical (turtle) environments, try my shareware shareware development environment "Newt" (newt-devenv-24.sit/.zip/.hqx on uiowa, amug, AOL, eWorld; newt24.sit/.zip on CIS).
  554.  
  555. Registered users receive a manual and a floppy of several dozen examples including Life (several versions).
  556. ----------
  557. Life.helpBook+page6
  558. .subject 1
  559. Contact Author
  560. .story
  561. Stephen Weyer
  562. 17 Timber Knoll Drive
  563. Washington Crossing, PA 18977-1052
  564. Internet: weyer@guest.apple.com
  565. America Online, eWorld, NewtonMail: SteveWeyer
  566. Compuserve: 74603,2051
  567. AppleLink: WEYER.S
  568. ----------
  569. BYE!
  570.